/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
+const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Mainloop = imports.mainloop;
const DocInfo = imports.misc.docInfo;
+const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
+const DASH_DOCS_ICON_SIZE = 16;
+
+const DEFAULT_SPACING = 4;
+
/* This class represents a single display item containing information about a document.
* We take the current number of seconds in the constructor to avoid looking up the current
* time for every item when they are created in a batch.
this._updateTimeoutTargetTime = -1;
this._updateTimeoutId = 0;
- this._docManager = DocInfo.getDocManager(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
+ this._docManager = DocInfo.getDocManager();
this._docsStale = true;
this._docManager.connect('changed', function(mgr, userData) {
me._docsStale = true;
};
Signals.addSignalMethods(DocDisplay.prototype);
+
+function DashDocDisplayItem(docInfo) {
+ this._init(docInfo);
+}
+
+DashDocDisplayItem.prototype = {
+ _init: function(docInfo) {
+ this._info = docInfo;
+ this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
+ spacing: DEFAULT_SPACING,
+ reactive: true });
+ this.actor.connect('button-release-event', Lang.bind(this, function () {
+ docInfo.launch();
+ Main.overview.hide();
+ }));
+
+ this._icon = docInfo.createIcon(DASH_DOCS_ICON_SIZE);
+ let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
+ iconBox.append(this._icon, Big.BoxPackFlags.NONE);
+ this.actor.append(iconBox, Big.BoxPackFlags.NONE);
+ let name = new Clutter.Text({ font_name: "Sans 14px",
+ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+ ellipsize: Pango.EllipsizeMode.END,
+ text: docInfo.name });
+ this.actor.append(name, Big.BoxPackFlags.EXPAND);
+
+ let draggable = DND.makeDraggable(this.actor);
+ this.actor._delegate = this;
+ },
+
+ getDragActorSource: function() {
+ return this._icon;
+ },
+
+ getDragActor: function(stageX, stageY) {
+ this.dragActor = this._info.createIcon(DASH_DOCS_ICON_SIZE);
+ return this.dragActor;
+ },
+
+ //// Drag and drop functions ////
+
+ shellWorkspaceLaunch: function () {
+ this._info.launch();
+ }
+}
+
+/**
+ * Class used to display two column recent documents in the dash
+ */
+function DashDocDisplay() {
+ this._init();
+}
+
+DashDocDisplay.prototype = {
+ _init: function() {
+ this.actor = new Shell.GenericContainer();
+ this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
+ this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
+ this.actor.connect('allocate', Lang.bind(this, this._allocate));
+
+ this._docManager = DocInfo.getDocManager();
+ this._docManager.connect('changed', Lang.bind(this, function(mgr) {
+ this._redisplay();
+ }));
+ this._redisplay();
+ },
+
+ _getPreferredWidth: function(actor, forHeight, alloc) {
+ let children = actor.get_children();
+
+ // We use two columns maximum. Just take the min and natural size of the
+ // first two items, even though strictly speaking it's not correct; we'd
+ // need to calculate how many items we could fit for the height, then
+ // take the biggest preferred width for each column.
+ // In practice the dash gets a fixed width anyways.
+
+ // If we have one child, add its minimum and natural size
+ if (children.length > 0) {
+ let [minSize, naturalSize] = children[0].get_preferred_width(forHeight);
+ alloc.min_size += minSize;
+ alloc.natural_size += naturalSize;
+ }
+ // If we have two, add its size, plus DEFAULT_SPACING
+ if (children.length > 1) {
+ let [minSize, naturalSize] = children[1].get_preferred_width(forHeight);
+ alloc.min_size += DEFAULT_SPACING + minSize;
+ alloc.natural_size += DEFAULT_SPACING + naturalSize;
+ }
+ },
+
+ _getPreferredHeight: function(actor, forWidth, alloc) {
+ let children = actor.get_children();
+
+ // Two columns, where we go vertically down first. So just take
+ // the height of half of the children as our preferred height.
+
+ let firstColumnChildren = children.length / 2;
+
+ alloc.min_size = 0;
+ for (let i = 0; i < firstColumnChildren; i++) {
+ let child = children[i];
+ let [minSize, naturalSize] = child.get_preferred_height(forWidth);
+ alloc.natural_size += naturalSize;
+
+ if (i > 0 && i < children.length - 1) {
+ alloc.min_size += DEFAULT_SPACING;
+ alloc.natural_size += DEFAULT_SPACING;
+ }
+ }
+ },
+
+ _allocate: function(actor, box, flags) {
+ let width = box.x2 - box.x1;
+ let height = box.y2 - box.y1;
+
+ let children = actor.get_children();
+
+ // The width of an item is our allocated width, minus spacing, divided in half.
+ let itemWidth = Math.floor((width - DEFAULT_SPACING) / 2);
+ let x = box.x1;
+ let y = box.y1;
+ let columnIndex = 0;
+ let i = 0;
+ // Loop over the children, going vertically down first. When we run
+ // out of vertical space (our y variable is bigger than box.y2), switch
+ // to the second column.
+ for (; i < children.length; i++) {
+ let child = children[i];
+
+ let [minSize, naturalSize] = child.get_preferred_height(-1);
+
+ if (y + naturalSize > box.y2) {
+ // Is this the second column? Ok, break.
+ if (columnIndex == 1) {
+ break;
+ }
+ // Set x to the halfway point.
+ columnIndex += 1;
+ x = x + itemWidth + DEFAULT_SPACING;
+ // And y is back to the top.
+ y = box.y1;
+ }
+
+ let childBox = new Clutter.ActorBox();
+ childBox.x1 = x;
+ childBox.y1 = y;
+ childBox.x2 = childBox.x1 + itemWidth;
+ childBox.y2 = y + naturalSize;
+
+ y = childBox.y2 + DEFAULT_SPACING;
+
+ child.show();
+ child.allocate(childBox, flags);
+ }
+
+ // Everything else didn't fit, just hide it.
+ for (; i < children.length; i++) {
+ children[i].hide();
+ }
+ },
+
+ _redisplay: function() {
+ this.actor.remove_all();
+
+ let docs = this._docManager.getItems();
+ let docUrls = [];
+ for (let url in docs) {
+ docUrls.push(url);
+ }
+ docUrls.sort(function (urlA, urlB) { return docs[urlB].timestamp - docs[urlA].timestamp; });
+ let textureCache = Shell.TextureCache.get_default();
+
+ for (let i = 0; i < docUrls.length; i++) {
+ let url = docUrls[i];
+ let docInfo = docs[url];
+ let display = new DashDocDisplayItem(docInfo);
+ this.actor.add_actor(display.actor);
+ }
+ }
+}
/**
* shell_texture_cache_evict_thumbnail:
* @cache:
- * @size: Size in pixels
* @uri: Source URI
*
- * Removes the reference the shell_texture_cache_load_thumbnail() function
- * created for a thumbnail.
+ * Removes all references added by shell_texture_cache_load_thumbnail() function
+ * created for the given URI.
*/
void
shell_texture_cache_evict_thumbnail (ShellTextureCache *cache,
- int size,
const char *uri)
{
- CacheKey key;
+ GHashTableIter iter;
+ gpointer key, value;
- memset (&key, 0, sizeof(key));
- key.size = size;
- key.thumbnail_uri = (char*)uri;
+ g_hash_table_iter_init (&iter, cache->priv->keyed_cache);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ CacheKey *cachekey = key;
+
+ if (cachekey->thumbnail_uri == NULL || strcmp (cachekey->thumbnail_uri, uri) != 0)
+ continue;
- g_hash_table_remove (cache->priv->keyed_cache, &key);
+ g_hash_table_iter_remove (&iter);
+ }
}
/**
* shell_texture_cache_evict_recent_thumbnail:
* @cache:
- * @size: Size in pixels
* @info: A recent info
*
- * Removes the reference the shell_texture_cache_load_recent_thumbnail() function
- * created for a thumbnail.
+ * Removes all references added by shell_texture_cache_load_recent_thumbnail() function
+ * for the URI associated with the given @info.
*/
void
shell_texture_cache_evict_recent_thumbnail (ShellTextureCache *cache,
- int size,
GtkRecentInfo *info)
{
- CacheKey key;
-
- memset (&key, 0, sizeof(key));
- key.size = size;
- key.thumbnail_uri = (char*)gtk_recent_info_get_uri (info);
-
- g_hash_table_remove (cache->priv->keyed_cache, &key);
+ shell_texture_cache_evict_thumbnail (cache, gtk_recent_info_get_uri (info));
}
static ShellTextureCache *instance = NULL;